"""进程池Pool
Pool类可以提供指定数量（一般为CPU的核数）的进程供用户调用，当有新的请求提交到Pool中时，如果池还没有满，就会创建一个新的进程来执行请求。如果池满，请求就会告知先等待，直到池中有进程结束，才会创建新的进程来执行这些请求。
如果操作的对象数目上百个甚至更多，那手动去限制进程数量就显得特别的繁琐，此时就可以交给进程池自动管理多个进程。
注意：进程池中的进程是不能共享队列和数据的，而Process生成的子进程可以共享队列
Pool类中常用方法

apply()：
函数原型：apply(func[, args=()[, kwds={}]])
该函数用于传递不定参数，主进程会被阻塞直到函数执行结束（不建议使用，并且3.x以后不再使用）。阻塞的，和单进程没有什么区别
apply_async():
函数原型：apply_async(func, args=(), kwds={}, callback=None, error_callback=None)
异步非阻塞的，不用等待当前进程执行完毕，随时根据系统调度来进行进程切换（建议使用）
注意：apply_async每次只能提交一个进程的请求
map()
函数原型：map(func, iterable[, chunksize=None])
Pool类中的map方法，与内置的map函数用法行为基本一致，它会使进程阻塞直到返回结果。
注意：
虽然第二个参数是一个迭代器，但在实际使用中，必须在整个队列都就绪后，程序才会运行子进程；
map返回的是一个列表，由func函数的返回值组成
close()
关闭进程池（Pool），使其不再接受新的任务。
terminate()
立刻结束工作进程，不再处理未处理的任务
join()
使主进程阻塞等待子进程的退出，join方法必须在close或terminate之后使用。
"""
from multiprocessing import Pool
import os, time


def process_run_1(num):
    print("Child process {} start, the number of this process is {}".format(num, os.getpid()))
    time.sleep(0.5)
    print("Child process {} end.")


if __name__ == "__main__":
    pool = Pool(5)  # 定义一个进程池，最大进程数5
    for i in range(1, 11):
        pool.apply_async(process_run_1, (i,))  # 使用非阻塞方式调用func

    print("Start child process")
    pool.close()  # 关闭进程池，不再接受新的请求
    pool.join()  # 等到pool中的所有子进程执行接受，必须放在close之后
    print("End")
